#include "HPCDatabase.h"
#include "QFile"
#include <QtSql/QSqlQuery>
#include <QTextStream>
#include <HPCLogger.h>
#include <QtXml/QDomElement>
#include <QtXml/QDomNode>
#include <QTextStream>
#include <QVariant>
#include <QtSql/QSqlRecord>
#include <QtSql/QSqlError>
#include <QDateTime>
#include <iostream>
#include "HPCSensors.h"

HPCDatabase::HPCDatabase(QString& host, QString& usr, QString& pwd, QString& dbname, QString& appPath)
    :QObject(), m_Host(host), m_User(usr), m_Pwd(pwd), m_DbName(dbname)
{
    m_CacheFile = appPath + ".cache";
    m_XMLConfigFile = appPath + ".config";
    LOG(info,"cachefile is " << m_CacheFile);
    LOG(info,"configxml is " << m_XMLConfigFile);

    connectAndRegister(true);
    if (m_State != 0){
        LOG(info, "write MeasureValues to file");
   }
}

HPCDatabase::~HPCDatabase()
{
    m_db.close();
    m_db.removeDatabase("QMYSQL");
}

bool HPCDatabase::connectAndRegister(bool first)
{
    if (first) {
        m_db = QSqlDatabase::addDatabase("QMYSQL");
        m_db.setHostName(m_Host);
        m_db.setDatabaseName(m_DbName);
        m_db.setUserName(m_User);
        m_db.setPassword(m_Pwd);
    }

    if (!m_db.open()) {
        this->m_State = 3;      // no conncetion to database
        LOG(warning,"error while opening the database - " << m_db.lastError().text());
        m_db.close();
        return false;
    }
    else {
        this->m_State = 1;       // connected
        LOG(info, "Database connection successful.");
        LOG(info, "    host: " << m_Host);
        LOG(info, "    usr: " << m_User);
        LOG(info, "    db: " << m_DbName);
        this->checkServer();

        if (this->m_State == 0)     // connect and logged on
        {
            LOG(info, "Server is registered and logged on.");

            checkAndWriteLocalMeasureValues();

        }
        else
        {
            LOG(trace, "Server not found in database.");
        }
        return true;
    }
}

bool HPCDatabase::getConfig(QDomDocument* docCfg)
{
    bool ok = false;

    if (this->m_State == 0)     // connect and logged on
    {
        ok = this->getConfigFromDB(docCfg);
    }
    else
    {
        ok = this->getConfigFromXML(docCfg);
    }
    return ok;
}

bool HPCDatabase::getConfigFromDB(QDomDocument* docCfg)
{
    QSqlQuery quCl(m_db);
    LOG(info,"getting the configfile form database");

        // root-element Server
    QDomElement elSrv = docCfg->createElement("server");
    elSrv.setAttribute("id",QString::number(m_ServerId));
    elSrv.setAttribute("title",m_ServerTitle);

    docCfg->appendChild(elSrv);

    if (!quCl.exec("SELECT id, Title FROM Clients WHERE ServerId= " + QString::number(this->m_ServerId))) {
        LOG(debug,"lastError: " << quCl.lastError().text());
    }

    // -----------
    // client-loop
    // -----------
    while  (quCl.next()) {
        QString title = quCl.value(1).toString();
        QDomElement elClient = docCfg->createElement("client");
        QSqlQuery quSens(m_db);
        LOG(debug, "Client:" << title);
        elClient.setAttribute("id",quCl.value(0).toString());
        elClient.setAttribute("title",quCl.value(1).toString());

        quSens.exec("Select Id, Title, TypeId, ValueType, MeasureUnit FROM Sensors WHERE ClientId = " + quCl.value(0).toString());
         // -----------------
         // sensor-loop
         // -----------------
         while  (quSens.next()) {
             QDomElement elSens = docCfg->createElement("sensor");
             QSqlQuery quAlert(m_db);
             QSqlQuery quParm(m_db);
             LOG(debug, "Sensor: " << quSens.value(1).toString());
             elSens.setAttribute("id",quSens.value(0).toString());
             elSens.setAttribute("title",quSens.value(1).toString());
             elSens.setAttribute("typeid",quSens.value(2).toString());
             elSens.setAttribute("valuetype",quSens.value(3).toString());
             elSens.setAttribute("measureunit",quSens.value(4).toString());

             quParm.exec("SELECT Title, Value FROM SensorParameters WHERE SensorId = " + quSens.value(0).toString());
             // -----------
             // param-loop
             // -----------
             while  (quParm.next()) {
                 elSens.setAttribute(quParm.value(0).toString(), quParm.value(1).toString());
             }

             quAlert.exec("SELECT id, title, MinVal, MaxVal FROM SensorAlerts WHERE SensorId = " + quSens.value(0).toString());
             // -----------
             // alert-loop
             // -----------
             while  (quAlert.next()) {
                QDomElement elAlert = docCfg->createElement("alert");
                QSqlQuery quAction;

                LOG(debug, "Alert: " << quAlert.value(1).toString());
                elAlert.setAttribute("title",quAlert.value(1).toString());
                elAlert.setAttribute("minvalue",quAlert.value(2).toString());
                elAlert.setAttribute("maxvalue",quAlert.value(3).toString());

                quAction.exec("SELECT id, Title, TypeId FROM SensorAlertActions WHERE AlertId = " + quAlert.value(0).toString());
                // -----------
                // action-loop
                // -----------
                while  (quAction.next()) {
                   QDomElement elAction = docCfg->createElement("action");
                   QSqlQuery quActParm;

                    LOG(debug, "Action: " << quAction.value(1).toString());
                    elAction.setAttribute("title",quAction.value(1).toString());
                    elAction.setAttribute("typeid",quAction.value(2).toString());

                    quActParm.exec("SELECT Title, Param FROM SensorAlertActionParameters WHERE ActionId = " + quAction.value(0).toString());
                    // -----------
                    // action-loop
                    // -----------
                    while  (quActParm.next()) {
                        QDomElement elActParm = docCfg->createElement("param");

                        LOG(debug, "Actionparameter: " << quActParm.value(0).toString());
                        elActParm.setAttribute("title",quActParm.value(0).toString());
                        elActParm.setAttribute("param",quActParm.value(1).toString());
                        elAction.appendChild(elActParm);
                    }
                    elAlert.appendChild(elAction);
                }
                elSens.appendChild(elAlert);
            }
            elClient.appendChild(elSens);
        }
        elSrv.appendChild(elClient);
    }

    docCfg->appendChild(elSrv);
    // Writing to the XML-Config File
    QFile file(m_XMLConfigFile);
    if(!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
        LOG(debug, "Open the XML-Config file for writing failed");
    }
    else {
        QTextStream stream(&file);
        stream << docCfg->toString();
        file.close();
        LOG(info,"Writing XML-Config file (" <<  m_XMLConfigFile << ") is done");
    }

    return true;
}

bool HPCDatabase::getConfigFromXML(QDomDocument* docCfg)
{
    QFile f(m_XMLConfigFile);
    if (  f.open(QIODevice::ReadOnly) && docCfg->setContent(&f) ) {
        f.close();
        QDomNode n = docCfg->firstChild();
        QDomElement e = n.toElement();
    }
    else {
         LOG(warning, "Serverconfigurationfile not found! (" << m_XMLConfigFile);
         return false;
    }

    return true;
}

void HPCDatabase::checkServer()
{
    QString macaddr;
    QString sql;
    QSqlQuery query(m_db);

    macaddr = this->getMacAddres();

    sql = "SELECT Id, Title FROM Servers WHERE maddr = \"" + macaddr + "\"";

    if (query.exec(sql)) {
        if (query.first()) {
            m_ServerTitle = query.value(1).toString();
            m_ServerId  = query.value(0).toInt();
            LOG(debug, "Server: " << m_ServerTitle);
            m_State = 0;
        }
        else {
            m_State = 3;			// Server not found
        }
    }
    else {
        LOG(error,"last error: " << query.lastError().text());
    }
}

QString HPCDatabase::getMacAddres()
{
    if (m_MacAdress.length() == 0)
    {
        QFile file("/sys/class/net/eth0/address");

        if (!file.open(QIODevice::ReadOnly | QIODevice::Text)){
           THROW( "HPCDatabase Server not found!" );
           return m_MacAdress;
        }

        QTextStream in(&file);
        if (!in.atEnd()) {
            m_MacAdress = in.readLine();
            LOG(info, "Server MacAddress: " << m_MacAdress);
        }
    }
    return m_MacAdress;
}

void HPCDatabase::writeMeasureValues(QString values)
{
    if (values.length() > 0) {
        // is the connection ready and the station is logged on
        if (m_State == 0)
        {
            writeMeasureValuesToDB (values);
        }
        else
        {
            LOG(warning, "connection lost - try to connect");
            connectAndRegister(false);
            if (m_State != 0)
            {
                writeMeasureValuesToFile(values);
            }
            else
            {
                writeMeasureValuesToDB(values);
            }
        }
    }
}

void HPCDatabase::writeMeasureValuesToDB (QString values)
{
    QString sql;
    QSqlQuery query(m_db);

    values.chop(2);
    sql = "INSERT INTO MeasureValues (SensorId, Value, CreationDateTime) VALUES " + values;

    if (!query.exec (sql)) {
        LOG(error, "error while writing measurevalue in the database : " << query.lastError().text());
        LOG(error, "SQL: " << sql);
        writeMeasureValuesToFile(values);
    }
}

void HPCDatabase::writeMeasureValuesToFile (QString values)
{
    QFile file(m_CacheFile);

    if (file.open(QIODevice::Append | QIODevice::Text)){
       QTextStream out(&file);
//       HPCValue v = val;
//       QString sv = v.V().toString();
//       QString ts = QDateTime::fromTime_t(val.TimeStamp()).toString("yyyy-MM-dd hh:mm:ss");
//       sv.replace(",",".");
//       out << "(" << id << ",'" <<  sv << "','" << ts << "'),";
       out << values;
       file.close();
    }
}

void HPCDatabase::writeActValue (uint id, const HPCValue& val)
{
    if (m_State == 0) {
        QString sql;
        QSqlQuery query(m_db);

        HPCValue v = val;

        sql = "UPDATE Sensors SET ActValue=\"" + v.V().toString() + "\" WHERE id = " + QString::number(id);
        if (!query.exec (sql)) {
            LOG(error, "error while writing actvalue in the database : " << query.lastError().text());
        }
    }
}

void HPCDatabase::checkAndWriteLocalMeasureValues()
{
    QFile file(m_CacheFile);
    QString line;

    if (file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&file);

        if (!in.atEnd())
        {
            if (in.Ok == QTextStream::Ok)
            {
                // lesen aus Datei -> schreiben in Database
                line = in.readLine();
                line.chop(2);
           }
        }
        file.close();

        if (line.size() > 0)
        {
            QString sql = "INSERT INTO MeasureValues (SensorId,Value,CreationDateTime) VALUES " + line;
            QSqlQuery query(m_db);

            if (!query.exec(sql))
            {
                LOG(error, "error while writing measurevalue from local file in the database : " << query.lastError().text() );
            }
            else
            {
                file.remove();
                LOG(debug, "file '" << m_CacheFile << "' was written to the database and the file was deleted.");
            }
        }
    }
    else
    {
        LOG(debug, "file '" << m_CacheFile << "' not available - nothing to do.");
    }
}


